// k1 编辑 2017.12.10 - 2021.11.28
/*
*	Fire-Fight Improvement System by Zooloo75
*	Causes incoming gunfire to scramble a group, simulating panic, fear, and surprise.
*	Units may accidentally fire their weapon out of fear from incoming fire (possibly resulting in friendly fire).
*	Units do evasive maneuvers to avoid gunfire.
*	Suppression is now effective against AI.
*	Higher skilled soldiers tend to regain focus quicker.
*	AI can now suppress other AI.
*	Units lose focus as they take fire, making them difficult to command.
*	Firefights last longer.
*	Groups attempt to flank their shooter(s) and continue engagement.
*	Units will pop smoke when under fire.
*	Sniper units react differently than non-sniper units.
*	Bleed-outs now result in actual blood loss instead of just behaving as a visual.
*	AI can call in artillery support.
*	Adds wind simulation to most types of projectiles.
*/

if (!isNil "ss_FFIS_LMG") exitWith {};

SS_FFIS_USESMOKE = true; //false 禁用AI丢烟雾弹
SS_FFIS_BLEEDOUTS = false; //false 禁用持续流血
SS_FFIS_MORTARS = true; //false 禁用AI呼叫迫击炮/火炮的能力 *地图上必须有AI操作的迫击炮
SS_FFIS_MORTARSDIS = 50; //呼叫炮火的AI距离准炮击点至少要超过多少米才会进行炮击
if (isNil "SS_FFIS_MORTARSProbability") then {SS_FFIS_MORTARSProbability = 950}; //呼叫迫击炮概率设为0会100%对同一地点多次打击（很频繁），默认的条件判断(random 1000 > 950) *过于频繁反而不合理建议500以上
SS_FFIS_BULLETWIND = false;	//false 禁用子弹风偏. Disabled by default in v1.1 to prevent interference with other mods (i.e. TMR)
SS_FFIS_EVASIVEANIMATIONS = true; //false 禁用AI躲避动作
SS_FFIS_WAYPOINTS = false; //false 禁用FFIS的路点 ***

SS_FFIS_LMGSUPPRESSION = false;	//false 禁用机枪单位的强制全自动火力，true时下面SS_FFIS...AMMO才有效
SS_FFIS_LMGINFINITEAMMO = true;	//false 禁用机枪单位无限弹药 (无限弹药有助于使机枪单位对压制有用！).

SS_FFIS_FIREDEHDISABLE = false; //true 关闭开火事件
SS_FFIS_FIREDProbability = 10; //开火每颗子弹触发概率
SS_FFIS_HITProbability = 40; //击伤触发概率

//=======================================================================
SS_FFIS_USERCONFIGEXISTS = true; //请勿改动

ss_FFIS_tt = if(SS_FFIS_BULLETWIND)then[{.3},{.5}]; //

ss_FFIS_remoteExec = {
	params ["_notLocalUnits", "_firer"];
	{if(local _x && {alive _x} && {vehicle _x == _x})then {[_x,_firer] call ss_FFIS_Behaviour}} forEach _notLocalUnits;
};

ss_FFIS_suppress = { //压制射击
	_this suppressFor (5 + random 10);
	_aimAccuracy = _this skill "aimingAccuracy";
	_this setSkill ["aimingAccuracy",_aimAccuracy/(1 + random 1)];
	sleep (10-((skill _this)*7));
	_this setSkill ["aimingAccuracy",skill _this];
};

ss_FFIS_LMG = {
	params ["_unit", "_target"];
	_initPos = position _unit;
	_lastPos = position _target;
	_lastPos2 = eyePos _target; //
	_fireRate = .03 + random .2;
	sleep (2 + random 2);
	_unit doWatch _target;
	_unit doTarget _target; //
	sleep (2 + random 1);
	for "_i" from 0 to (30 + round(random 200)) do {
		if(_initPos distance (position _unit) > 3 || {[_unit, "VIEW", _target] checkVisibility [eyePos _unit, _lastPos2] isEqualTo 0} || {!alive _unit}) exitWith {}; //
		if(SS_FFIS_LMGINFINITEAMMO) then {_unit setVehicleAmmo 1};
		_unit doWatch _lastPos;
		_unit forceWeaponFire [currentWeapon _unit, "FullAuto"];
		sleep (_fireRate + random .1);
	};
};

ss_FFIS_artyFire = {
	(_this select 0) doArtilleryFire [(_this select 1), "8Rnd_82mm_Mo_shells", round (1 + random 5)];
};

ss_FFIS_setUnitPos = {
	params ["_unit", "_t", "_sw1", "_sw2"];
	_t = time + _t;
	waitUntil {time > _t || {!alive _unit} || {!isNull objectParent _unit}};
	if (!alive _unit) exitWith {};
	if (_sw1 && {isNull objectParent _unit}) then {
		_move = selectRandom ["AmovPpneMevaSlowWrflDf", "amovppnemstpsraswrfldnon"];
		_unit playMoveNow _move;
	};
	if (_sw2) then {_unit setUnitPos "AUTO"};
};

ss_FFIS_MoveComb1 = {
	_this playMoveNow "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldl";
	_this playMove "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldl";
};
ss_FFIS_MoveComb2 = {
	_this playMoveNow "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldr";
	_this playMove "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldr";
};
ss_FFIS_MoveComb3 = {
	_this playMoveNow "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldl";
	_this playMove "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldr";
};
ss_FFIS_MoveComb4 = {
	_this playMoveNow "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldl";
	_this playMove "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldl";
	_this playMove "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldr";
};
ss_FFIS_MoveComb5 = {
	_this playMoveNow "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldr";
	_this playMove "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldr";
	_this playMove "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldl";
};
ss_FFIS_MoveComb6 = {
	_this playMoveNow "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldl";
	_this playMove "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldl";
	_this playMove "amovppnemstpsraswrfldnon_aadjppnemstpsraswrfldleft";
};
ss_FFIS_MoveComb7 = {
	_this playMoveNow "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldr";
	_this playMove "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldr";
	_this playMove "amovppnemstpsraswrfldnon_aadjppnemstpsraswrfldright";
};

ss_FFIS_checkHouse = {
	_objs = lineIntersectsWith [eyePos _this, _this modelToWorld [0, 0, (eyePos _this select 2) + 10], _this];
	_r = if(count _objs == 0)then[{true},{!((_objs select 0) isKindOf "House")}];
	_r
};

ss_FFIS_Behaviour = {
	params ["_unit", "_firer"];
	if !(_unit getVariable ["ss_FFIS_Check2", true]) exitWith {};
	
	_weaponType = getText (configFile >> 'CfgWeapons' >> currentWeapon _unit >> 'cursor');
	(group _unit) setBehaviour (if(_weaponType == "srifle")then[{"STEALTH"},{"AWARE"}]);//

	_unit setVariable ["ss_FFIS_Check2",false, true]; //防止多次快速执行
	_unit spawn {sleep (.5 + random 4.5); _this setVariable ["ss_FFIS_Check2",true, true]};
	
	if(isPlayer _unit || {_unit getVariable ["ss_FFIS_Check",false]}) exitWith {};
	_grp = _unit getVariable ["ss_FFIS_Group",group _unit];
	_chance = random 100;
	if (SS_FFIS_USESMOKE && {_weaponType != "srifle"} && {_chance < 5}) then {
		_smoke = [];
		{ if((_x select 0) in magazines _unit)then{_smoke pushback (_x select 1)} } forEach [["SmokeShell","SmokeShellMuzzle"],["SmokeShellBlue","SmokeShellBlueMuzzle"],["SmokeShellYellow","SmokeShellYellowMuzzle"],["SmokeShellGreen","SmokeShellGreenMuzzle"],["SmokeShellPurple","SmokeShellPurpleMuzzle"],["SmokeShellOrange","SmokeShellOrangeMuzzle"],["SmokeShellRed","SmokeShellRedMuzzle"]];
		if (count _smoke == 0) exitWith {};
		_SSM = _smoke select 0; //selectRandom _smoke
		_unit forceWeaponFire [_SSM,_SSM]; //[_unit,_SSM] call BIS_fnc_fire; //强制武器开火 全局函数可能受限
		if ({isPlayer _x} count units _unit > 0) then {[_unit, "烟雾弹掷出!"] remoteExec ["groupChat", 0]};
	};
	
	if (_chance > 90 && {{isPlayer _x} count units _unit > 0}) then {
		_chance = floor(random 6);
		_str = switch _chance do {
			case 0: {format["救命! 我被压制在 %1", mapGridPosition getPosASL _unit]};
			case 1: {format["向这边开火 %1", mapGridPosition getPosASL _unit]};
			case 2: {"遇袭!"};
			case 3: {"采取掩护!"};
			case 4: {"我被压制住了!"};
			case 5: {"有人再向我开火!"};
			default {""};
		};
		[_unit, _str] remoteExec ["groupChat", 0];
	};
	
	if(!SS_FFIS_FIREDEHDISABLE)then {_unit setVariable ["ss_FFIS_Check",true]};//
	_randomPos = [(getPosASL _unit select 0) + random 60 - random 60,(getPosASL _unit select 1) + random 60 - random 60];
	_unit setUnitPos (if(_unit distance _randomPos <= 20 || {_weaponType == "srifle"})then[{[_unit, 60 + random 120, false, true] spawn ss_FFIS_setUnitPos; "DOWN"},{"AUTO"}]);////
	doStop _unit;
	[_unit,_grp,_firer] spawn {
		params ["_unit", "_grp", "_firer"];
		_posFirer = position _firer;
		if(_posFirer distance [0,0,0] < 100) exitWith {_unit setVariable ["ss_FFIS_Check",false]};//
		_aimAccuracy = _unit skill "aimingAccuracy";
		_courage = _unit skill "courage";
		_commanding = _unit skill "commanding";
		{
			_x setSkill ["aimingAccuracy",random .2 + ((skill _x)/(2 * random 4))]; 
			_x suppressFor (5 + random 20); 
			if (SS_FFIS_LMGSUPPRESSION && {(getText(configfile >> "CfgWeapons" >> currentWeapon _x >> "cursor")) == "mg"}) then {[_x,_firer] spawn ss_FFIS_LMG};
		} forEach units group _unit;
		
		if (SS_FFIS_WAYPOINTS) then {
			_grp addWaypoint [_posFirer, 0]; 
			[_grp, 0] setWaypointType "SAD"; //modified from Destroy waypoint which may make units attack static objects
			[_grp, 0] setWaypointBehaviour "UNCHANGED"; // changed from COMBAT to UNCHANGED
			sleep 5;
			//_grp setCurrentWaypoint [_grp, 0];
		};
		
		_unit setSkill ["courage",(skill _unit)/4];
		_unit setSkill ["commanding",0];
		sleep (10 - ((skill _unit) * (1 + random 8)));
		_unit setSkill ["aimingAccuracy",_aimAccuracy];
		_unit setSkill ["courage",_courage];
		_unit setSkill ["commanding",_commanding];
		_initGroup = _unit getVariable ["ss_FFIS_Group",_grp];
		_unit doTarget _firer;
		_unit doFire _firer;
		_unit setVariable ["ss_FFIS_Check",false];
		_chance = random 1000;
		if (SS_FFIS_MORTARS && {_chance > SS_FFIS_MORTARSProbability} && {!isNil "ss_FFIS_Artillery"}) then {
			_sideArty = [];
			for "_i" from 0 to ((count ss_FFIS_Artillery) - 1) do {
				_x = ss_FFIS_Artillery select _i;
				if (side _x == side _unit && {_unit distance2D _posFirer > SS_FFIS_MORTARSDIS} && {alive _x}) then {_sideArty pushback _x};
			};
			if (count _sideArty > 0) then {
				_arty = _sideArty select (round(random((count _sideArty) - 1)));
				if (isPlayer _arty) exitWith {};
				[_unit, format["请求迫击炮打击坐标 %1.",mapGridPosition _posFirer]] remoteExec ["sideChat", 0];
				sleep (3 + random 5);
				[gunner _arty, "请求批准. 炮击指定位置!"] remoteExec ["sideChat", 0];
				sleep (1 + random 2);
				[_arty, _posFirer] remoteExec ["ss_FFIS_artyFire", _arty];
			};
		};
	};
	_unit doMove _randomPos; //向附近移动
	(group _unit) setSpeedMode "FULL";
	
	if (currentWeapon _unit in ["","Throw"] || {(getPosASL _unit select 2) < 0} || {lifeState _unit == "INCAPACITATED"} || {_unit distance _firer < 30} || {!isNull objectParent _unit}) exitWith {}; //在水里不执行动作
	_chance = random 500;
	if (_chance > 50) then {
		if (SS_FFIS_EVASIVEANIMATIONS && {_weaponType != "srifle"} && {currentWeapon _unit == primaryWeapon _unit}) then {
			switch (stance _unit) do {
				case "STAND": { //站姿
					switch floor(random 5) do {
						case 0: { //右闪躲并蹲下，有穿墙bug
							if (getPosATL _unit select 2 < .5 && {_unit call ss_FFIS_checkHouse}) then {
								_unit playMoveNow "AmovPercMevaSrasWrflDfr_AmovPknlMstpSrasWrflDnon";
								_unit setUnitPos "MIDDLE";
								[_unit, 10 + random 50, false, true] spawn ss_FFIS_setUnitPos;
							};
						};
						case 1: { //站立到右侧躺
							_unit playMoveNow "AmovPercMstpSrasWrflDnon_AadjPpneMstpSrasWrflDright";//
							_unit setUnitPos "DOWN"; //强制单位趴下
							[_unit, 3 + random 3, true, true] spawn ss_FFIS_setUnitPos;
						};
						case 2: { //站立到趴下
							_unit playMoveNow "amovpercmsprslowwrfldf_amovppnemstpsraswrfldnon"; //适用蹲姿
							_unit setUnitPos "DOWN";
							[_unit, 4 + random 6, false, true] spawn ss_FFIS_setUnitPos;
						}; ////
						case 3: { //站立到左侧躺
							_unit playMoveNow "AmovPercMstpSrasWrflDnon_AadjPpneMstpSrasWrflDleft";//
							_unit setUnitPos "DOWN";
							[_unit, 3 + random 3, true, true] spawn ss_FFIS_setUnitPos;
						};
						case 4: { //左闪躲并蹲下，有穿墙bug
							if (getPosATL _unit select 2 < .5 && {_unit call ss_FFIS_checkHouse}) then {
								_unit playMoveNow "AmovPercMevaSrasWrflDfl_AmovPknlMstpSrasWrflDnon";
								_unit setUnitPos "MIDDLE";
								[_unit, 10 + random 50, false, true] spawn ss_FFIS_setUnitPos;
							};
						};
					};
				};
				case "CROUCH": { //蹲姿
					switch floor(random 2) do {
						case 0: {_unit playMoveNow "aadjpknlmstpsraswrflddown"};
						case 1: {
							_unit playMoveNow "amovppnemstpsraswrfldnon";
							_unit setUnitPos "DOWN";
							[_unit, 20 + random 100, false, true] spawn ss_FFIS_setUnitPos;
						};
					};
				};
				case "PRONE": { //匍匐
					switch floor(random 14) do {
						case 0: {_unit playMoveNow "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldl"};
						case 1: {_unit playMoveNow "amovppnemsprslowwrfldl"};
						case 2: {_unit call ss_FFIS_MoveComb7; [_unit, 4 + random 6, true, false] spawn ss_FFIS_setUnitPos;};//
						case 3: {_unit playMoveNow "amovppnemstpsraswrfldnon_aadjppnemstpsraswrfldleft"; [_unit, 3 + random 3, true, false] spawn ss_FFIS_setUnitPos;};//
						case 4: {_unit call ss_FFIS_MoveComb3};
						case 5: {_unit playMoveNow "amovppnemstpsraswrfldnon_amovppnemevaslowwrfldr"};
						case 6: {_unit playMoveNow "amovppnemstpsraswrfldnon_aadjppnemstpsraswrfldright"; [_unit, 3 + random 3, true, false] spawn ss_FFIS_setUnitPos;};//
						case 7: {_unit call ss_FFIS_MoveComb5};
						case 8: {_unit call ss_FFIS_MoveComb2};
						case 9: {_unit playMoveNow "amovppnemstpsraswrfldnon_aadjppnemstpsraswrflddown"; [_unit, 3 + random 3, true, false] spawn ss_FFIS_setUnitPos;};//
						case 10: {_unit call ss_FFIS_MoveComb1};
						case 11: {_unit playMoveNow "amovppnemsprslowwrfldr"};
						case 12: {_unit call ss_FFIS_MoveComb6; [_unit, 4 + random 6, true, false] spawn ss_FFIS_setUnitPos;};//
						case 13: {_unit call ss_FFIS_MoveComb4};
					};
				};
				default {};
			};
		};
		if(_chance > 400) then {_unit forceWeaponFire [currentWeapon _unit, "Single"]};////
	};
};

ss_FFIS_FiredEH = {
_firer = _this select 0; //开火的单位
if (!ss_FFIS_Switch || {SS_FFIS_FIREDEHDISABLE && {!(_firer isKindOf "StaticMortar")}} || {count _this < 8}) exitWith {};
if (time > (_firer getVariable ["ss_FFIS_t1", 0])) then {
	_firer setVariable ["ss_FFIS_t1", time + ss_FFIS_tt]; //
	_unit = _this select 7; //开火的步兵单位
if (local _unit) then {
	_projectile = _this select 6;
	if(!alive _projectile) exitWith {};
	_time = time +10;
	while {alive _projectile && {time < _time}} do { //弹着点10米内单位的行为举止
		_nearUnits = nearestObjects [getPosATL _projectile,["CaManBase"],10];
		_notLocalUnits = [];
		{
			if (alive _x && {side _x != side _firer} && {_x getVariable ["ss_FFIS_Check2", true]} && {(random 100) < SS_FFIS_FIREDProbability} && {_x getVariable ['FAR_once_eh',true]} && {_x getVariable ['FAR_isUnconscious',0] == 0} && {!(_x getVariable ["AT_Revive_isUnconscious",false])} && {!(_x getVariable ["FAR_CallAiRevive", false])} && {lifeState _x != "INCAPACITATED"}) then { //50
				if (!isPlayer _x) then {if (local _x) then [{[_x,_firer] call ss_FFIS_Behaviour},{_notLocalUnits pushback _x}]};
				if (!isPlayer _unit) then {_unit spawn ss_FFIS_suppress};
			}; 
		} forEach _nearUnits;
		if (count _notLocalUnits > 0) then {
			[_notLocalUnits, _firer] remoteExec ["ss_FFIS_remoteExec", 0];
		};
		
		sleep .02;
		
		//子弹风偏
		if (SS_FFIS_BULLETWIND) then {
			_mag = _this select 5;
			_filter = getText (configFile >> 'CfgMagazines' >> _mag >> 'nameSound');
			if (_filter == 'magazine' || {_filter == "mgun"} || {_filter == "rockets"} || {_filter == "cannon"} || {_filter == "heat"}) then {
				_velX = velocity _projectile select 0;
				_velY = velocity _projectile select 1;
				_velZ = velocity _projectile select 2;
				_windX = (wind select 0)/10;
				_windY = (wind select 1)/10;
				_projectile setVelocity [(_velX + random _windX) + random 2 - random 2,(_velY + random _windY) + random 2 - random 2, _velZ];
			};
		};
	};
}}};

ss_FFIS_HitEH = {
	_Hit = _this select 0;
	if (!ss_FFIS_Switch || {!(_Hit getVariable ['FAR_once_eh',true])} || {_Hit getVariable ['FAR_isUnconscious',0] == 1} || {_Hit getVariable ["AT_Revive_isUnconscious",false]} || {_Hit getVariable ["FAR_CallAiRevive", false]} || {lifeState _Hit == "INCAPACITATED"}) exitWith {};//
	if (time > (_Hit getVariable ["ss_FFIS_t2", 0])) then {
		_Hit setVariable ["ss_FFIS_t2", time + .6]; //
		if (_Hit getVariable ["ss_FFIS_Check2", true] && {vehicle _Hit == _Hit} && {alive _Hit} && {local _Hit} && {(random 100) < SS_FFIS_HITProbability}) then { //70
			_Hit setVariable ["ss_FFIS_Check", false];
			[_Hit, objNull] spawn ss_FFIS_Behaviour;
		};
	};
};

ss_FFIS_AddUnit = {
	_this addEventHandler ["Fired", {_this spawn ss_FFIS_FiredEH}]; //重生有效
	_this addEventHandler ["Hit", {_this spawn ss_FFIS_HitEH}]; //重生有效
	_this setVariable ["ss_FFIS_Group", group _this];
	
	if (SS_FFIS_BLEEDOUTS) then { //持续掉血
		_this spawn {
			while {alive _this} do {
				if (isBleeding _this || {damage _this > .85}) then {
					_this setDamage ((damage _this) + (.02 + random .1));
				};
				sleep (5 + random 5);
			};
		};
	};
};

ss_FFIS_AddVeh = {
	_role = "";
	_turretPath = [];
	_cargoIndex = 0;
	_veh = vehicle _this;
	{if (_x select 0 == _this) exitWith {_role = _x select 1; _turretPath = _x select 3; _cargoIndex = _x select 2}} forEach fullCrew _veh; //_role = assignedVehicleRole _this;
	if (toLower _role in ["turret","gunner","commander"] && {_cargoIndex == -1} && {count(_veh weaponsTurret _turretPath) != 0}) then {
		_veh setVariable ["zooloo75_FFIS_veh", false];
		_this spawn {
			_veh = vehicle _this;
			_index = _veh addEventHandler ["Fired", {_this spawn ss_FFIS_FiredEH}];
			
			if (_veh isKindOf "StaticMortar") then {
				if (isNil "ss_FFIS_Artillery") then {ss_FFIS_Artillery = []};
				ss_FFIS_Artillery pushback _veh;
				waitUntil {sleep 2; !alive _veh || {!alive _this} || {!(_this in _veh)}};
				ss_FFIS_Artillery = ss_FFIS_Artillery - [_veh];
				_veh removeEventHandler ["fired", _index];
				_veh setVariable ["zooloo75_FFIS_veh", nil];
			};
		};
	};
};

ss_FFIS_Loop = {
	waitUntil {!isNil "BIS_fnc_init"};////
	if (!isNil "SS_FFIS_Only_one") exitWith {};//
	SS_FFIS_Only_one = 1;
	[] spawn {
		while {ss_FFIS_Switch} do {
			{
				if (local _x && {alive vehicle _x} && {_x isKindOf "CaManBase"} && {toLower typeOf _x find "zombie" == -1}) then {
			        if (vehicle _x == _x) then [{
					    if (_x getVariable ["zooloo75_FFIS_unit", true]) then {
					    	_x setVariable ["zooloo75_FFIS_unit", false];
					    	_x call ss_FFIS_AddUnit;
					    };
				    },{
					    if (vehicle _x getVariable ["zooloo75_FFIS_veh", true]) then {
						    _x call ss_FFIS_AddVeh;
					    };
				    }];
				};
			} forEach allUnits; //
			_t = time + 30;
			waitUntil {!ss_FFIS_Switch || {time > _t}};
		};
		SS_FFIS_Only_one = nil;
	};
};

if (isNil "SS_FFIS_Switch") then {SS_FFIS_Switch = true};
call ss_FFIS_Loop;

ss_FFIS_Ready = true;


